package com.ymx.darling.datasource;

import com.ymx.darling.datasource.defaults.PooledDataSource;
import com.ymx.darling.exception.DarlingException;
import com.ymx.darling.exception.ExceptionFactory;
import com.ymx.darling.exception.ExceptionWrapper;
import com.ymx.darling.util.ReflectUtil;
import com.ymx.darling.util.StringUtil;

import javax.sql.DataSource;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * DataSource构造器
 */
public class DataSourceBuilder {
    /**
     * 根据数据库配置信息构建DataSource
     * @param property DataSourceProperty
     * @return DataSource
     */
    public static DataSource build(DataSourceProperty property) {
        try {
            Class<?> dataSourceClass = getDataSourceClass(property);
            return createDataSource(dataSourceClass, property.getPropertyMap());
        } catch (Exception e) {
            String title = "构建DataSource实例时抛出异常";
            throw ExceptionFactory.wrapException(title, new ExceptionWrapper(e, e.getMessage()));
        }
    }


    private static Class<?> getDataSourceClass(DataSourceProperty property) throws ClassNotFoundException {
        String dataSourceClassName = property.getDataSourceClassName();
        if (StringUtil.isNull(dataSourceClassName)) {
            return PooledDataSource.class;
        } else {
            Class<?> dataSourceClass = Class.forName(dataSourceClassName);
            if (!DataSource.class.isAssignableFrom(dataSourceClass)) {
                throw new DarlingException("类:"+dataSourceClassName+"不是"+DataSource.class.getName()+"的实现");
            }
            return dataSourceClass;
        }
    }


    /**
     * 创建DataSource实例并设置属性值
     * @param dataSourceClass class
     * @param property map
     * @return DataSource
     */
    private static DataSource createDataSource(Class<?> dataSourceClass, Map<String, String> property) {
        DataSource dataSource = ReflectUtil.newInstance(dataSourceClass);
        Map<Method, String> setMethodMap = getMethodMap(dataSourceClass, property);
        for (Method method : setMethodMap.keySet()) {
            String strValue = setMethodMap.get(method);
            try {
                setValueToMethod(dataSource, method, strValue);
            } catch (Exception e) {
                String errMsg = "调用方法"+method+"抛出异常:";
                throw new DarlingException(e, errMsg+e.getMessage());
            }
        }

        return dataSource;
    }


    /**
     * 为DataSource赋值
     * @param dataSource dataSource
     * @param method method
     * @param value value
     */
    private static void setValueToMethod(DataSource dataSource, Method method, String value) {
        Class<?> parameterType = method.getParameterTypes()[0];
        Object paramValue = ReflectUtil.convertBasicType(parameterType, value);
        ReflectUtil.invoke(method, dataSource, paramValue);
    }


    /**
     * 根据用户设置的字段返回对应的set方法
     * @param dataSourceClass class
     * @param propertyMap map
     * @return map
     */
    private static Map<Method, String> getMethodMap(Class<?> dataSourceClass, Map<String, String> propertyMap) {
        // 遍历所有set方法
        Map<String, Method> methodMap = new HashMap<>();
        for (Method method : dataSourceClass.getMethods()) {
            String methodName = method.getName();
            if (StringUtil.isSetterMethod(methodName) && method.getParameterCount() == 1) {
                String methodToProperty = StringUtil.methodToProperty(methodName);
                methodMap.put(methodToProperty, method);
            }
        }
        // 找出设置DataSource的字段方法
        Map<Method, String> propertySetMethodMap = new HashMap<>();
        propertyMap.forEach((propertyName, strValue) -> {
            Method method = methodMap.get(propertyName);
            if (Objects.nonNull(method)) {
                propertySetMethodMap.put(method, strValue);
            } else {
                String setMethodName = propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1);
                throw new DarlingException(String.format("类:%s找不到方法set%s", dataSourceClass.getName(), setMethodName));
            }
        });

        return propertySetMethodMap;
    }
}
